﻿-- 이 프로그램은 tags 파일 내에서 태그를 찾아 파일을 open해줍니다.

import System
import System.IO
import System.Directory
import Control.Monad
import Data.Char
import Data.List
import Safe

-- (검색된) 태그들을 보여주면서 사용자가 태그를 선택하면 파일을 보여준다.
interactive tagLines = do
    printLines tagLines
    putStr "? "
    s <- getLine
    when (s=="q") $ do
        error "user aborted"
    let n = parseNo s (length tagLines - 1)
    when (n>=0) $ do launchFile (tagLines!!n)
    interactive tagLines
    where 
        printLines strs = mapM_ (putStrLn . addLineNum) $ zip [0..] tagLines
            where addLineNum (n, ln) = show n ++ " " ++ ln
        parseNo str nMax = if n>=0 && n<=nMax then n else -1
            where 
                n = if n'==Nothing then -1 else unjust n'
                    where n' = readMay str::Maybe Int
                unjust (Just x) = x

-- "tags" 파일에서 태그를 검색한다.
doFindTags::Bool->String->IO [String]
doFindTags wholeWord tag = do
    tags <- fmap lines $ readFile "tags"
    let cmpFn = if wholeWord then (==) else isInfixOf
    let result = filter (match tag cmpFn) tags
    return result
    where
        match::String->(String->String->Bool)->String->Bool
        match t cmp ln = cmp t' s
            where
                [t'] = fmap (map toUpper) [t]
                [s] = fmap (map toUpper) (take 1 (words ln))
                

--launchFile::String->IO ()
launchFile tagLine = do
    let file = words tagLine !! 1
    let str = between "/^" "$/;\"" tagLine
    -- print str
    when (str/=[]) $ do
        system $ "start scite \"-open:" ++ file ++ "\" \"-find:" ++ str ++ "\""
        return ()
    where
        between _ _ [] = []
        between s1 s2 str@(x:xs) =
            if s1 `isPrefixOf` str
              then reverse $ takeBefore s2 (drop (length s1) str) ""
              else between s1 s2 xs
        takeBefore _ [] rlt = []
        takeBefore s str@(x:xs) rlt =
            if s `isPrefixOf` str
              then rlt
              else takeBefore s xs (x:rlt)

findTags wholeWord tag = do
    tags <- doFindTags wholeWord tag
    if length tags == 0 then return ()
      else if length tags == 1 then launchFile (head tags)
        else interactive tags

-- vim 에서 사용되는 명령어 tj를 흉내냄
tj = findTags False

-- vim 에서 사용되는 명령어 ta를 흉내냄
ta = findTags True

usage = do
    putStrLn "usage: tj [-w] <keyword>"
    putStrLn "This program can search tag line from tags. For more help, google ctags."

main = do
    ex <- doesFileExist "tags"
    when (not ex) $ do
        system "ctags --recurse=yes *.*"
        return ()
    args <- getArgs
    let opts = filter (\s->head s == '-') args
    let tags = filter (\s->head s /= '-') args
    if tags==[] 
      then usage
      else do
        if filter (=="-w") opts /= []
          then ta (head tags)
          else tj (head tags)